package com.zh.webflux.log;


import com.zh.webflux.util.RequestParamWrapper;
import com.zh.webflux.log.annotation.OperateLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.web.reactive.function.server.ServerRequest;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 操作记录打印
 *
 * @author ZH
 * @date 16:03 2020/6/22
 */
@Slf4j
@Aspect
public class OperatingLogAspect {
    private final OperationLogRecorder operationLogRecorder;

    public OperatingLogAspect(OperationLogRecorder operationLogRecorder) {
        this.operationLogRecorder = operationLogRecorder;
        log.info("[OperatingLogAspect] constructor ...");
    }

    @Pointcut("execution(public * com.zh.webflux.web..*(..)) && @annotation(operateLog)")
    public void optLogPointcut(OperateLog operateLog) {
    }

    /**
     * 前置通知
     * 记录用户的访问操作
     */
    @Before("optLogPointcut(operateLog)")
    public void doBefore(JoinPoint joinPoint, OperateLog operateLog) {
        String remoteHost = getRemoteHost(joinPoint);
        String operation = operateLog.value();
        String uri = getRequestURI(joinPoint);
        String queryParams = queryParams(joinPoint);

        log.info(">>> accessLog. remoteHost = {}, operation = {}, requestURI = {}", remoteHost, operation, uri);
        operationLogRecorder.accessLog(remoteHost, operation, uri, queryParams);
    }

    /**
     * 获取request的请求参数
     *
     * @param joinPoint
     * @return
     */
    private String queryParams(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            ServerRequest request = (ServerRequest) args[0];
            Map<String, Object> map = new HashMap<>();

            request.queryParams().forEach((key, value) -> {
                value.forEach(item -> {
                    map.merge(key, item, (x, y) -> new LinkedList<>(List.of(x, y)));
                });
            });
            return map.toString();
        }
        return "";
    }

    /**
     * 获取request的请求formData
     *
     * @param joinPoint
     */
    private Mono<Map<String, Object>> formDataParams(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            ServerRequest request = (ServerRequest) args[0];

            ///x-www-form-urlencoded 数据
            Mono<Map<String, Object>> formParams = request.formData().map(formData -> {
                Map<String, Object> map = new HashMap<>();
                formData.forEach((key, value) -> {
                    value.forEach(item -> {
                        map.merge(key, item, (x, y) -> new LinkedList<>(List.of(x, y)));
                    });
                });
                return map;
            });

            //form-data 数据
            Mono<Map<String, Object>> formParams2 = request.multipartData().map(formData -> {
                Map<String, Object> map = new HashMap<>();

                RequestParamWrapper.toMultiValueMap(formData)
                        .forEach((key, value) -> {
                            value.forEach(item -> {
                                map.merge(key, item, (x, y) -> new LinkedList<>(List.of(x, y)));
                            });
                        });
                return map;
            });

            return formParams.flatMap(e -> {
                if (e.isEmpty()) {
                    return formParams2;
                } else {
                    return Mono.just(e);
                }
            });
        }
        return Mono.just(Map.of());
    }

    /**
     * 获取请求来自客户端host
     *
     * @return
     */
    private String getRemoteHost(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            ServerRequest req = (ServerRequest) args[0];
            boolean present = req.remoteAddress().isPresent();
            if (present) {
                return req.remoteAddress().get().getHostName();
            }
        }
        return "";
    }

    /**
     * 获取请求的URI
     *
     * @return
     */
    private String getRequestURI(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            ServerRequest req = (ServerRequest) args[0];
            return req.uri().getPath();
        }
        return "";
    }
}